/*
 * vector.h
 *
 *  Created on: 05.11.2011
 *      Author: ignacio
 */

#ifndef VECTOR_H_
#define VECTOR_H_

#include <iostream>
using std::ostream;


#include <ClanLib/Core/Math/vec3.h>
// http://clanlib.org/docs/clanlib-2.3/reference_doxygen/classCL__Vec3.html

//
// ****************
// * ID_AfVector3 *
// ****************
//
namespace neurignacio{
	class ID_Angle;						// Predefinition of ID_Angle
	typedef float real;					// Alias to the type used as real
	typedef CL_Vec3<real> BaseVector3; 	// Alias to the class from which the definitive "Vector3" is built
	class ID_fVector3;					// My Custom Class
	typedef ID_fVector3 Vector3;		// Alias to the class used in IAMillington

	class ID_fVector3 : public BaseVector3
	{

		public:
		// constructors
		ID_fVector3(void) : BaseVector3(0.0f) {}
		ID_fVector3(const real& x, const real& y, const real& z=0) : BaseVector3(x,y,z) {}
		ID_fVector3(const BaseVector3& v) : BaseVector3(v) {}
		ID_fVector3(const BaseVector3& from, const BaseVector3& to) : BaseVector3(to.x-from.x, to.y-from.y, to.z-from.z) {}
		// operators
		friend ostream& operator<<(ostream& out, const ID_fVector3& vector);
		ID_fVector3 operator*(const real& f) {return static_cast<BaseVector3>(*this)*f;}
		ID_fVector3 operator*(const int& i) {return static_cast<BaseVector3>(*this)*i;}
		ID_fVector3 operator+(const ID_fVector3& vector) const {return static_cast<BaseVector3>(*this)+static_cast<BaseVector3>(vector);}
		ID_fVector3 operator+(const float& f) const {return static_cast<BaseVector3>(*this)+ID_fVector3(f);}
		ID_fVector3 operator+(const int& f) const {return static_cast<BaseVector3>(*this)+ID_fVector3(f);}
		void operator+=(const ID_fVector3& vector) {*this=(*this)+vector;}
		void operator+=(const float& f) {*this=(*this)+f;}
		void operator+=(const int& f) {*this=(*this)+f;}
		ID_fVector3 operator-(const ID_fVector3& vector) const {return static_cast<BaseVector3>(*this)-static_cast<BaseVector3>(vector);}
		ID_fVector3 operator-(const float& f) const {return static_cast<BaseVector3>(*this)-ID_fVector3(f);}
		ID_fVector3 operator-(const int& f) const {return static_cast<BaseVector3>(*this)-ID_fVector3(f);}
		void operator-=(const ID_fVector3& vector) {*this=(*this)-vector;}
		void operator-=(const float& f) {*this=(*this)-f;}
		void operator-=(const int& f) {*this=(*this)-f;}
//		Pointer<Vector3> operator&() const {return shared_from_this();}
//		ID_fVector3 operator=(const ID_fVector3 &rhs) {if (this!=&rhs) this->x=rhs.x; this->y=rhs.y; this->z=rhs.z; return *this;}
		// ***********************
		// functions from Vector3
		// ***********************

		//Calculates and returns a component-wise product of this vector with the given vector.
		ID_fVector3 componentProduct(const Vector3 &v) const {return Vector3(x*v.x, y*v.y, z*v.z);}

		// Performs a component-wise product with the given vector and sets this vector to its result.
		void componentProductUpdate(const Vector3 &vector) {x *= vector.x; y *= vector.y; z *= vector.z;}

		// Calculates and returns the vector product of this vector with the given vector.
		Vector3 vectorProduct(const Vector3 &vector) const {return Vector3(y*vector.z-z*vector.y, z*vector.x-x*vector.z,x*vector.y-y*vector.x);}

		// Updates this vector to be the vector product of its current value and the given vector.
		void operator %=(const Vector3 &vector) {*this = vectorProduct(vector);}

		// Calculates and returns the vector product of this vector with the given vector.
		Vector3 operator%(const Vector3 &vector) const {return Vector3(y*vector.z-z*vector.y,z*vector.x-x*vector.z,x*vector.y-y*vector.x);}

		// Calculates and returns the scalar product of this vector with the given vector.
		real scalarProduct(const Vector3 &vector) const {return x*vector.x + y*vector.y + z*vector.z;}

		// Calculates and returns the scalar product of this vector with the given vector.
		real operator*(const Vector3 &vector) const {return x*vector.x + y*vector.y + z*vector.z;}

		// Adds the given vector to this, scaled by the given amount.
		void addScaledVector(const Vector3& vector, real scale) {x += vector.x * scale; y += vector.y * scale; z += vector.z * scale;}

		// Gets the magnitude of this vector.
		real magnitude() const{ return length(); }

		// Gets the squared magnitude of this vector.
		real squareMagnitude() const {return x*x+y*y+z*z;}

		// Turns a non-zero vector into a vector of unit length.
		void normalise() {normalize();}

		// Returns a unit vector in the direction of this vector.
		Vector3 unit() const {Vector3 result = *this; result.normalize(); return result;}

		void setMagnitude(real magnitude){normalize(); (*this) *= magnitude;}

		// Zero all the components of the vector.
		void clear(){x = y = z = 0;}

		// Flips all the components of the vector.
		void invert() {x = -x; y = -y; x = -z;}

		// Checks if the vector is zero */
		bool isZero() const {return x == 0 && y == 0 && z == 0;}

		// Return the angle of the components X and Z of a vector
		ID_Angle angleXZ() const;

		// Return the angle of the vector
		ID_Angle angle() const;

		// Sets a random direction
		void random(unsigned int seed=0);
	};

	// constants and useful definitions
	typedef unsigned char uint8;			// Alias to the type used as unsigned 8-bit integer
	typedef unsigned int uint16;		// Alias to the type used as unsigned 16-bit integer
	const real REAL_MAX=3.40282e+038f;
	const Vector3 NULL_VECTOR(0,0,0);
} // end namespace neurignacio

//
// ************************
// * Additional Functions *
// ************************
//

namespace neurignacio{
	float sin(const ID_Angle& theta);
	float cos(const ID_Angle& theta);
	Vector3 pow(const Vector3& vector, const float& exponent);
	ID_Angle pow(const ID_Angle& theta, const float& exponent);
	float pow(const float& base, const float& exponent);
	ID_Angle atan2(const float& y, const float& x);
	ID_Angle abs(const ID_Angle& theta);
} // end namespace neurignacio

//
// ************
// * ID_Angle *
// ************
//
#include <ClanLib/Core/Math/angle.h>

namespace neurignacio{
	#define radians cl_radians
	#define degrees cl_degrees
	#define DEFAULT_UNIT degrees

	typedef CL_Angle BasicAngle;	// Alias to the class from which the definitive "ID_Angle" is built. It should be an Angle Class like CL_Angle
	class ID_Angle;					// My Custom Angle Class
	typedef ID_Angle angle;			// Alias to the class used in IAMillington
	typedef CL_AngleUnit angleUnit;	// Alias to the type used as angle unit

	class ID_Angle : public BasicAngle
	{
		public:
		// Contructors
		ID_Angle(const real &theta, const angleUnit &unit=DEFAULT_UNIT) : CL_Angle(theta, unit){}
		ID_Angle(const int &theta=0, const angleUnit &unit=DEFAULT_UNIT) : CL_Angle(theta, unit){}
		ID_Angle(const CL_Angle& angle) : CL_Angle(angle) {}
		ID_Angle(const ID_Angle& angle) : CL_Angle(static_cast<CL_Angle>(angle)) {}
		// operators
		friend ostream& operator<<(ostream& out, const ID_Angle& angle) {out << angle.to_degrees();}
		ID_Angle operator*(const ID_Angle& angle) const {return static_cast<BasicAngle>(*this)*static_cast<BasicAngle>(angle);}
		ID_Angle operator*(const float& f) const {return static_cast<BasicAngle>(*this)*f;}
		ID_Angle operator*(const int& f) const {return static_cast<BasicAngle>(*this)*f;}
		void operator*=(const ID_Angle& angle) {*this=(*this)*angle;}
		void operator*=(const float& f) {*this=(*this)*f;}
		void operator*=(const int& f) {*this=(*this)*f;}
		ID_Angle operator/(const ID_Angle& angle) const {return static_cast<BasicAngle>(*this)/static_cast<BasicAngle>(angle);}
		ID_Angle operator/(const float& f) const {return static_cast<CL_Angle>(*this)/f;}
		ID_Angle operator/(const int& f) const {return static_cast<CL_Angle>(*this)/f;}
		void operator/=(const ID_Angle& angle) {*this=(*this)/angle;}
		void operator/=(const float& f) {*this=(*this)/f;}
		void operator/=(const int& f) {*this=(*this)/f;}
		ID_Angle operator+(const ID_Angle& angle) const {return static_cast<CL_Angle>(*this)+static_cast<CL_Angle>(angle);}
		ID_Angle operator+(const float& f) const {return static_cast<CL_Angle>(*this)+ID_Angle(f);}
		ID_Angle operator+(const int& f) const {return static_cast<CL_Angle>(*this)+ID_Angle(f);}
		void operator+=(const ID_Angle& angle) {*this=(*this)+angle;}
		void operator+=(const float& f) {*this=(*this)+f;}
		void operator+=(const int& f) {*this=(*this)+f;}
		ID_Angle operator-(const ID_Angle& angle) const {return static_cast<CL_Angle>(*this)-static_cast<CL_Angle>(angle);}
		ID_Angle operator-(const Vector3& vector) const {ID_Angle angle(vector.angleXZ()); angle.normalize(); angle=*this-angle; angle.normalize_180(); return angle;}
		ID_Angle operator-(const float& f) const {return static_cast<CL_Angle>(*this)-ID_Angle(f);}
		ID_Angle operator-(const int& f) const {return static_cast<CL_Angle>(*this)-ID_Angle(f);}
		void operator-=(const ID_Angle& angle) {*this=(*this)-angle;}
		void operator-=(const Vector3& vector) {*this=(*this)-vector;}
		void operator-=(const float& f) {*this=(*this)-f;}
		void operator-=(const int& f) {*this=(*this)-f;}
		bool operator!(void) const {return !this->to_radians();}
		bool operator>(const ID_Angle& angle) const {return this->to_radians()>angle.to_radians();}
		bool operator<(const ID_Angle& angle) const {return this->to_radians()<angle.to_radians();}
		bool operator>=(const ID_Angle& angle) const {return this->to_radians()>=angle.to_radians();}
		bool operator<=(const ID_Angle& angle) const {return this->to_radians()<=angle.to_radians();}
		bool operator==(const ID_Angle& angle) const {return this->to_radians()==angle.to_radians();}
		bool operator!=(const ID_Angle& angle) const {return this->to_radians()!=angle.to_radians();}
		bool isZero() const {return *this==0;}
		signed char sign() const {return (*this<0?-1:1);}
		Vector3 asVector() const {return Vector3(neurignacio::cos(*this), 0, neurignacio::sin(*this));}
	};

	// constants
	const ID_Angle TWO_PI_ANGLE(360,degrees);
	const ID_Angle PI_ANGLE(180, degrees);
	const ID_Angle HALF_PI_ANGLE(90, degrees);
	const ID_Angle NULL_ANGLE(0,degrees);

} // end namespace neurignacio


//
// ************************
// * Additional Functions *
// ************************
//
namespace neurignacio{
	float sqrt(const ID_Angle& theta, const angleUnit& unit=DEFAULT_UNIT);
} // end namespace neurignacio

#endif /* VECTOR_H_ */
